//#define PROFILEINDENT

//
// CODEBOXES
// This implements a vaguely EMACS-like code editor window using the RichTextBox class.
// It supports emacs-like regions as first-class objects (see Region.cs), however, these
// are implemented internally using the RichTextBox selection operations, so please
// don't screw with those directly.
//

//
// TODO:
//
// - Make undo less eggregiously inefficient
// - Save style info in the source files
// - Make printing handle rich text
//
// BUGS:
//

using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Globalization;
using System.Xml;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Diagnostics;
using Meta;

namespace Meta.Editor {
	public delegate void Thunk();

	public class CodeBox : ExRichTextBox {
		/// <summary>
		/// Static members
		/// </summary>
		[DllImport("user32.dll")]
		private static extern bool LockWindowUpdate(int hWnd);

#if OLD_SCROLLHACKING
		// Alas, none of this seems to have any effect
		[DllImport("user32.dll")]
		public static extern int GetScrollPos(int hWnd, int nBar);
		[DllImport("user32.dll")]
		public static extern int SetScrollPos(int hWnd, int nBar, int nPos, bool redraw);
		// From PlatformSDK/Include/WinUser.h
		public const int SB_HORZ = 0;
		public const int SB_VERT = 1;
#endif

		public event Thunk VerticalPositionChanged;
		public event Thunk SpacePressed;

		// Stylistic parameters
		static ColorConverter colorConverter = new ColorConverter();

		// Lexical and syntactic constants
		public const char startOfCommentChar = '\u00ab';
		public const char endOfCommentChar = '\u00bb';
		public const string startOfCommentString = "\u00ab";
		public const string endOfCommentString = "\u00bb";
		public static readonly char[] commentChars = new Char[2] { '\u00ab', '\u00bb' };

		// Key substititions (e.g. changing '-' and '>' into the lambda arrow
		static Hashtable keySubstitutions = new Hashtable();

		public static int highlightPeriod = 500;
		public static int reindentTimeout = 2000;

		/// <summary>
		/// Instance memebers
		/// </summary>
		/// 

		System.ComponentModel.IContainer components = null;

		// Command-processing state variables
		bool greekNext;                            // Next char should be treated as a Greek letter
		public bool iSearchMode=false;               // In i-search mode
		int iSearchStartPoint;
		string iSearchString;

		Style nextStyle;

		// Parsing-related state variables
		Region currentTopLevelRegion = new Region(0,0);
		internal bool reIndent = true;
        public bool onlyTopLevelDefs = Editor.OnlyTopLevelDefs;
        public bool blankLinesMarkTopLevelRegions = Editor.BlankLinesMarkTopLevelRegions;
		public bool undoEnabled = true;

#if SYNTAXHIGHLIGHTING
		bool reComment;                          // Rerun the commenting code code over this line (I think -ian)
#endif
		System.Windows.Forms.Timer unhighlightTimer;
		System.Windows.Forms.Timer reindentTimer;
		public ToolTip tooltip;

		// Other state variables
		System.Collections.Stack undoStack = new System.Collections.Stack();
		bool ModifiedSinceUndoMark = false;

		public Editor EditorWindow {
			get {
				return FindForm() as Editor;
			}
		}

		public void SetStatusLine(string str) {
			EditorWindow.SetArglistText(str);
		}

		public Namespace Namespace {
			get {
				return EditorWindow.Namespace;
			}
		}

		/// <summary>
		/// Constructors
		/// </summary>
		public CodeBox() {
			// This call is required by the Windows Form Designer.
			InitializeComponent();
			
			AcceptsTab = true;
			DetectUrls = true;
			AutoWordSelection = false;   // It would be nice to have this, but it often triggers accidentally and it also tends to include brackets in the word.
			AllowDrop = true;

			foreach (DictionaryEntry e in defaultStyleTable)
				RegisterStyle((string)e.Key, (Style)e.Value);
			Font = TheStyle("Normal").font;
			
			TabIndex = 4;
			Text = "";
			WordWrap = false;
			unhighlightTimer = new System.Windows.Forms.Timer();
			unhighlightTimer.Stop();
			unhighlightTimer.Tick += new EventHandler(Unhighlight);
			unhighlightTimer.Interval = highlightPeriod;
			reindentTimer = new System.Windows.Forms.Timer();
			reindentTimer.Stop();
			reindentTimer.Tick += new EventHandler(ReindentHandler);
			reindentTimer.Interval = reindentTimeout;
			tooltip=new ToolTip();
            tooltip.ShowAlways = false;
            tooltip.InitialDelay = 1000;
            tooltip.AutomaticDelay = 1000;
            tooltip.AutoPopDelay = 5000;
            tooltip.ReshowDelay = 1000;
            tooltip.UseAnimation = true;
            tooltip.UseFading = true;

#if SYNTAXHIGHLIGHTING
			BackColor = EditorConfiguration.backColor;
			ForeColor = EditorConfiguration.foreColor;
#else
			BackColor = Editor.defaultBackgroundColor;
#endif
			//VerticalPositionChanged += new Thunk(ReindentCurrentLine);
			VerticalPositionChanged += new Thunk(IndentTopLevelExpression);
			TextChanged += new EventHandler(HandleTextChanged);
		}

		static CodeBox() {
			DefineKeySubstitution("<-", "\u2190");
			DefineKeySubstitution("<=", "\u2264");
			DefineKeySubstitution(">=", "\u2265"); 
			DefineKeySubstitution("<<", startOfCommentString); 
			DefineKeySubstitution(">>", endOfCommentString); 
			DefineKeySubstitution("->", "\u2192");
			DefineKeySubstitution("\u2212>", "\u2192");
			DefineKeySubstitution("/=", "\u2260");
			DefineKeySubstitution("=/", "\u2260");
			DefineKeySubstitution("!=", "\u2260");
			DefineKeySubstitution("sqrt", "\u221a");
			DefineKeySubstitution("==", "\u2261");
			//DefineKeySubstitution("/==", "\u2262");
			//DefineKeySubstitution("*=", "\u225b");
			//DefineKeySubstitution("\u00d7=", "\u225b");
			DefineKeySubstitution("~~", "\u2248");
			DefineKeySubstitution("infinity", "\u221e");
			DefineKeySubstitution("<>", "\u25ca");
			defaultStyleTable["Normal"] = Editor.defaultNormalStyle;
			defaultStyleTable["Title"] = Editor.defaultTitleStyle;
			defaultStyleTable["Comment"] = Editor.defaultCommentStyle;
			ReadDefaults();
		}

		public void RecordDefaults() {
			Editor.defaultBackgroundColor = BackColor;
			defaultStyleTable.Clear();
			foreach (DictionaryEntry e in styleTable)
				defaultStyleTable[e.Key] = e.Value;
			SaveDefaults();
		}

		static string defaultsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData)
			+ Path.DirectorySeparatorChar + "Meta" + Path.DirectorySeparatorChar + "EditorConfig";
		static void SaveDefaults() {
			Color back = Editor.defaultBackgroundColor;
			object[] styles = new Object[defaultStyleTable.Count+1];
			styles[0] = "styles";
			int i=1;
			foreach (DictionaryEntry e in defaultStyleTable) {
				object[] style = new Object[7];
				style[0] = e.Key;
				Style s = (Style)e.Value;
				Font f = s.font;
				Color c = s.color;
				style[1] = f.Name;
				style[2] = f.Size;
				style[3] = (int)f.Style;
				style[4] = c.R;
				style[5] = c.G;
				style[6] = c.B;
				styles[i++] = style;
			}
			object[] configuration = new object[] { new object[4] { "color", back.R, back.G, back.B },
													  styles,
													  new object[] { "OptimizeTailCalls", Expression.OptimizeTailCalls.Value},
													  new object[] {"ShowAllExpressions", EvalStack.showAllExpressions}, 
													  new object[] {"ShowDetail", Inspector.showDetail},
													  new object[] {"FocusOnWrite", Listener.focusOnWrite},
													  new object[] {"InsertMatchingBrackets", Editor.insertMatchingBrackets},
													  //													  new object[] {"IndentDefsLispStyle", Editor.indentDefsLispStyle},
													  new object[] {"FastEditorMode", Editor.fastMode},
													  new object[] {"ImportBasic2D", Meta.TopLevel.AlwaysOpen2D}
												  };

			string dir = Path.GetDirectoryName(defaultsPath);
			if (!System.IO.Directory.Exists(dir))
				System.IO.Directory.CreateDirectory(dir);

			StreamWriter file = new StreamWriter(defaultsPath);
			Writer t = new Writer(file);
			t.Write(configuration);
			file.Close();
		}

		static void ReadDefaults() {
			//try {
			//string defaultsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData)
			//	+ Path.DirectorySeparatorChar + "Meta" + Path.DirectorySeparatorChar + "EditorConfig";

            if (System.IO.Directory.Exists(Path.GetDirectoryName(defaultsPath)) && System.IO.File.Exists(defaultsPath))
            {
                StreamReader file = new StreamReader(defaultsPath);
                Reader r = new Reader(file, 0);
                object[] configuration = (object[])r.Read();
                foreach (object[] item in configuration)
                {
                    string type = (string)item[0];
                    switch (type)
                    {
                        case "styles":
                            defaultStyleTable.Clear();
                            for (int i = 1; i < item.Length; i++)
                            {
                                object[] description = (object[])item[i];
                                string styleName = (string)description[0];
                                string fontName = (string)description[1];
                                float fontSize = Convert.ToSingle(description[2]);
                                FontStyle fs = (FontStyle)Convert.ToInt32(description[3]);
                                Color c = Color.FromArgb(255, (int)description[4], (int)description[5], (int)description[6]);
                                defaultStyleTable[styleName] = new Style(new Font(fontName, fontSize, fs), c);
                            }
                            break;

                        case "color":
                            Editor.defaultBackgroundColor = Color.FromArgb(255, (int)item[1], (int)item[2], (int)item[3]);
                            break;

                        case "OptimizeTailCalls":
                            Expression.OptimizeTailCalls.DefaultValue = item[1];
                            break;

                        case "ShowAllExpressions":
                            EvalStack.showAllExpressions = (bool)item[1];
                            break;

                        case "ShowDetail":
                            Inspector.showDetail = (bool)item[1];
                            break;

                        case "FocusOnWrite":
                            Listener.focusOnWrite = (bool)item[1];
                            break;

                        case "InsertMatchingBrackets":
                            Editor.insertMatchingBrackets = (bool)item[1];
                            break;

                        //							case "IndentDefsLispStyle":
                        //								Editor.indentDefsLispStyle = (bool)item[1];
                        //								break;

                        case "FastEditorMode":
                            Editor.fastMode = (bool)item[1];
                            break;

                        case "ImportBasic2D":
                            TopLevel.AlwaysOpen2D = (bool)item[1];
                            break;
                    }
                }
                file.Close();
            }
            else
            {
                DialogResult answer = MessageBox.Show("Would you like to turn on beginning programmer mode?  This will:\n- Load the 2D graphics package.  This makes procedures\n   like box and translate available automatically.\n- Disable tail-call optimization.  This makes debugging easier\n   for those who haven't learned about tail-call optimization.\n\nSelecting Yes will enable beginning programmer mode whenever you run Meta.\n\nYou can disable it by checking/unchecking the items you want it in the Advanced menu.  To disable it forever, disable it and then choose Save As Defaults from the File menu.\n\nSelecting No will turn off beginning programmer mode (again, you can change it using the Advanced menu and Save As Defaults)\n\nSelecting Cancel will turn it off but ask you again when you rerun Meta.",
                    "Configure Meta", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);
                if (answer != DialogResult.Cancel)
                {
                    if (answer == DialogResult.Yes)
                    {
                        Expression.OptimizeTailCalls.DefaultValue = false;
                        Inspector.showDetail = false;
                        EvalStack.showAllExpressions = true;
                        Meta.TopLevel.AlwaysOpen2D = true;
                    }
                    SaveDefaults();
                }
            }
			//} catch (Exception e) {
			//	MessageBox.Show("Error reading editor configuration file", e.Message);
			//}
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing ) {
			// This is incredibly stupid, but the native dispose method actually calls the
			// convert-to-rtf code to serialize it into an in-core string.  This can take hundreds
			// of megs of virtual memory when we dispose a Listener window with lots of
			// images.
			Clear();
			// It's not good if these go off after the buffer is closed...
			unhighlightTimer.Stop();
			reindentTimer.Stop();
			if( disposing ) {
				if (components != null) {
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		public bool autoStyleTitles = true;
		public bool autoStyleComments = true;
		public class Style {
			public Font font;
			public Color color;

			public Style(Font font, Color color) {
				this.font = font;
				this.color = color;
			}

			public Style(string fontFamilyName, float fontSize, FontStyle fontStyle, Color color) {
				font = new Font(fontFamilyName, fontSize, fontStyle);
				this.color = color;
			}
		}

		static Hashtable defaultStyleTable = new Hashtable();
		Hashtable styleTable = new Hashtable();
		public Style TheStyle(string name) {
			return styleTable[name] as Style;
		}
		public void RegisterStyle(string name, Style s) {
			styleTable[name] = s;
		}
		
		//
		// Key substition (for handling multi-key combinations like ->)
		//

		class SubstitutionInfo {
			public string prefix;
			public string rewrite;
			public SubstitutionInfo(string prefix, string rewrite) {
				this.prefix = prefix;
				this.rewrite = rewrite;
			}
		}

		public static void DefineKeySubstitution(string from, string to) {
			char lastChar = from[from.Length-1];
			string prefix = from.Substring(0, from.Length-1);

			object probe = keySubstitutions[lastChar];
			ArrayList subs = (probe==null)?new ArrayList():(ArrayList)probe;

			subs.Add(new SubstitutionInfo(prefix, to));
			if (probe==null)
				keySubstitutions[lastChar] = subs;
		}


		//
		// Check if key is the second key of a two-key sequence and handle it
		// Returns true if it handled the event.
		//
		bool MaybeHandleCharacterSubstitution(char key) {
			object probe = keySubstitutions[key];

			// Easy cases are table-driven
			if (probe != null)
				foreach (SubstitutionInfo subst in (ArrayList)probe)
					if (MaybeChangeChar(subst.prefix, subst.rewrite))
						return true;

			// Harder cases require looking at context.
			switch (key) {
				case '-':
					return MaybeInsertOperator(Meta.TopLevel.minusSymbol);

				case '*':
					return MaybeInsertOperator(Meta.TopLevel.timesSymbol);

				case '/':
					return MaybeInsertOperator(" "+Meta.TopLevel.divisionSymbol+" ");

				case '"':
					// We don't know whether to insert an open quote or a close quote so we have to check the context.
					if (Editor.insertMatchingBrackets) {
						SetRegionText(userSelection, "\u201c\u201d");
					} else {
						CodeScanner s=ScanCharsFrom(userSelection.start);
						SetRegionText(userSelection,
							new string(s.FindCorrectQuoteMark('\u201c'),1));
					}
					userSelection.start += 1;
					userSelection.length = 0;
					return true;
			}

			object matching = CodeScanner.matchingBracket[key];
			if (Editor.insertMatchingBrackets && matching!=null
				&& (char.GetUnicodeCategory(key)==UnicodeCategory.OpenPunctuation
				|| char.GetUnicodeCategory(key)==UnicodeCategory.InitialQuotePunctuation)) {
				SetRegionText(userSelection, new string(new char[] {key, (char) matching}));
				userSelection.start += 1;
				userSelection.length = 0;

				return true;
			}

			return false;
		}


		[DllImport("user32.dll", EntryPoint="SendMessage", CharSet=CharSet.Auto)] 
		private static extern IntPtr SendMessageRedraw(IntPtr hWnd, Int32 msg, Int32 wParam, Int32 lParam);

		private const int WM_SETREDRAW = 0xB; 
		void SetRedraw(bool redrawEnabled) {
			SendMessageRedraw(Handle, WM_SETREDRAW, Convert.ToInt32(redrawEnabled), 0);
		}

		int lockCount=0;
		public void LockWindow() {
			if (lockCount==0) {
				//LockWindowUpdate((int)this.Handle);
				SetRedraw(false);
			}
			lockCount++;
		}

		public void AssertNotMultiplyLocked() {
			Debug.Assert(lockCount<2, "Window multiply locked when it shouldn't be");
		}

		public void MaybeUnlockWindow() {
			MaybeUnlockWindow(true);
		}

		public void MaybeUnlockWindow(bool checkStillLocked) {
			if (WindowLocked)
				UnlockWindow();
			Debug.Assert(!checkStillLocked || !WindowLocked, "MaybeUnlockWindow used when window multiply locked");
		}
		public void UnlockWindow() {
			System.Diagnostics.Debug.Assert(lockCount>0, "Unlocking buffer that's already unlocked!");
			lockCount--;
			if (lockCount==0) {
				//LockWindowUpdate(0);
				SetRedraw(true);
				Invalidate();
			}
		}

		public void UnlockWindowDammit() {
			if (lockCount!=0) {
				lockCount = 0;
				SetRedraw(true);
				Invalidate();
			}
		}

		public bool WindowLocked {
			get {
				return lockCount>0;
			}
		}

		char ReorderForGreek(char c) {
			switch (c) {
				case 'a':	  // alpha
				case 'b':  // beta
				case 'd':  // delta
				case 'e':  // epsilon
				case 'i':   // iota
				case 'o':  // omicron
				case 'p':  // pi
				case 's':  // sigma
				case 't':   // tau
				case 'u': // phi
					return c;

				case 'g':   // gamma
					return 'c';
				case 'z':  // zeta/Z
					return 'f';
				case 'h':  // Eta/H
					return 'g';
				case 'j':   // theta
					return 'h';
				case 'k':  // kappa
					return 'j';
				case 'l':   // lambda
					return 'k';
				case 'm':  // mu
					return 'l';
				case 'n':  // nu
					return 'm';
				case 'r':  // rho
					return 'q';
				case 'q':  // sigma final
					return 'r';
				case 'w':
					return 'y';
				case 'x': // chi
					return 'w';
				case 'y': // psi
					return 'x';
				
				default:
					return (char)0;
			}
		}

		char ToGreek(char c) {
			char reordered = ReorderForGreek(Char.ToLower(c));
			if (reordered==0)
				return c;
			else if (c>='A' && c<='Z')
				return (char)(reordered-'a'+'\u0391');
			else if (c>='a' && c<='z')
				return (char)(reordered-'a'+'\u03b1');
			else
				return c;
		}

		ArrayList completionResults = new ArrayList();
		string currentCompletion = null;
		public void TryCompletion(string prefix, string name) {
			if (name.StartsWith(prefix)) {
				if (!completionResults.Contains(name))
					completionResults.Add(name);
				if (currentCompletion==null)
					currentCompletion=name;
				else {
					// need to shorten the completion
					for (int i=0; i<Math.Min(name.Length,currentCompletion.Length); i++)
						if (name[i]!=currentCompletion[i]) {
							// Found where they diverge
							currentCompletion=currentCompletion.Substring(0, i);
						}
					if (name.Length<currentCompletion.Length)
						// If we get here, then name must be a prefix of currentCompletion
						currentCompletion=name;
				}
			}
		}

		protected void TryBindings(string prefix, IDictionary bindings) {
			if (bindings==null)
				return;
			foreach (DictionaryEntry e in bindings) {
				Symbol s=e.Key as Symbol;
				if (s!=null)
					TryCompletion(prefix, s.name);
				else
					TryCompletion(prefix, ((GlobalVariable)e.Key).name.name);
			}
		}

		protected void TryMembers(string prefix, Type type) {
			MemberInfo[] members = type.GetMembers();
			if (members!=null)
				foreach (MemberInfo m in members) {
					MethodInfo meth = m as MethodInfo;
					ConstructorInfo con = m as ConstructorInfo;

					if (con==null && (meth==null || !meth.IsSpecialName))
						TryCompletion(prefix, m.Name);
				}
		}

		protected string CompleteSymbol(string prefix) {
			currentCompletion=null;
			TryBindings(prefix, Namespace.bindings);
			foreach (NamespaceBase n in this.Namespace.Imports)
				TryBindings(prefix, n.Exports);
			CompletionWalker.WalkCompletions(this, TopLevelExpressionRegion(userSelection.Start).Start, prefix);
			return (currentCompletion!=null)?currentCompletion:prefix;
		}

		public void BreakStringAtLastDot(string source, out string prefix, out string suffix) {
			int lastdot = source.LastIndexOf('.');
			if (lastdot==source.Length) {
				prefix=source.Substring(source.Length-1);
				suffix="";
			} else {
				prefix=source.Substring(0, lastdot);
				suffix=source.Substring(lastdot+1, source.Length-(lastdot+1));
			}
		}

		public object LookupMemberExpression(string e) {
			if (e.IndexOf(".")>=0) {
				string objectExpression;
				string memberName;
				BreakStringAtLastDot(e, out objectExpression, out memberName);
				object o=LookupMemberExpression(objectExpression);
				if (o is NamespaceBase)
					return ((NamespaceBase)o).CheckBinding(memberName);
				else if (o != null) {
					object result = null;
					try {
						MemberInfo[] m = o.GetType().GetMember(memberName);
						if (m!=null && m.Length==1) {
							if (m[0] is FieldInfo)
								result = ((FieldInfo)m[0]).GetValue(o);
							else if (m[0] is PropertyInfo)
								result = ((PropertyInfo)m[0]).GetValue(o, new object[0]);
						} 
					} catch {}
					return result;
				} else
					return Namespace.CheckBinding(e);
			}
			return Namespace.CheckBinding(e);
		}

		protected string CompleteMember(string prefix) {
			string objectExpression;
			string memberPrefix;
			BreakStringAtLastDot(prefix, out objectExpression, out memberPrefix);
			Type guessedType = TypeGuesser.GuessType(this, TopLevelExpressionRegion(userSelection.start).start, objectExpression);
			currentCompletion=null;
			if (guessedType==null) {
				object memberObject=LookupMemberExpression(objectExpression);
				if (memberObject!=null) {
					if (memberObject is NamespaceBase)
						TryBindings(memberPrefix, ((NamespaceBase)memberObject).Exports);
					else {
						if (memberObject is Type)
							TryMembers(memberPrefix, (Type)memberObject);
						if (completionResults.Count>0)
							completionResults.Add(null);
						TryMembers(memberPrefix, memberObject.GetType());
					}
				}
			} else {
				TryMembers(memberPrefix, guessedType);
			}
			return objectExpression+'.'+((currentCompletion==null)?memberPrefix:currentCompletion);
		}

		void CompleteFromMenuItem(object sender, EventArgs e) {
			string prefix=CurrentVariableNameOrMemberReference;
			int lastDot=prefix.LastIndexOf('.');
			if (lastDot>=0)
				prefix = prefix.Substring(lastDot+1, prefix.Length-(lastDot+1));
			MenuItem m=(MenuItem)sender;
			string completion=(m.Text==prefix)?m.Text:(m.Text.Substring(0,prefix.Length)+m.Text.Substring(prefix.Length+1, m.Text.Length-(prefix.Length+1)));

			if (completion.StartsWith(prefix)) {
				NoteUserSelection();
				SetRegionText(userSelection, completion.Substring(prefix.Length, completion.Length-prefix.Length));
			}
		}

		void Complete() {
			NoteUserSelection();
			UnlockWindowDammit();
			completionResults.Clear();
			string prefix=CurrentVariableNameOrMemberReference;
			currentCompletion = null;
			string completion=prefix;
			if (prefix!="") {
				if (prefix.IndexOf('.')>=0)
					completion = CompleteMember(prefix);
				else
					completion=CompleteSymbol(prefix);
				if (completion!=prefix)
					SetRegionText(userSelection, completion.Substring(prefix.Length, completion.Length-prefix.Length));
				else if (completionResults.Count>0) {
					int sortStart=0;
					while (sortStart<completionResults.Count) {
						// Find separator, if any
						int sortEnd=completionResults.IndexOf(null, sortStart);
						if (sortEnd<0)
							sortEnd = completionResults.Count;
						completionResults.Sort(sortStart, sortEnd-sortStart, System.Collections.Comparer.Default);
						sortStart = sortEnd+1;
					}
					EventHandler completer=new EventHandler(CompleteFromMenuItem);
					ContextMenu m=new ContextMenu();
					int lastDot=prefix.LastIndexOf('.');
					if (lastDot>=0)
						prefix = prefix.Substring(lastDot+1, prefix.Length-(lastDot+1));
					foreach (object o in completionResults) {
						string s=(o!=null)?(string)o:null;
						MenuItem i = new MenuItem();
						if (o==null)
							i.Text="-";
						else if (s==prefix)
							i.Text=s;
						else {
							string start=s.Substring(0,prefix.Length);
							string end=s.Substring(prefix.Length,s.Length-(prefix.Length));
							i.Text = start+"&"+end;
						}
						i.Click += completer;
						m.MenuItems.Add(i);
					}
					m.Show(this, GetPositionFromCharIndex(userSelection.start));
				}
			}
		}
		
		//
		// Principle routine for handling typing.  Called after HandleKeyDown and before HandleKeyUp.
		// Also called before the base class gets to process the event.  Setting e.Handled to true
		// will prevent the base class from handling the key press.
		//
		protected override void OnKeyPress(KeyPressEventArgs e) {
			// Interesting.  This gets called *after* the low level COM object has *already* handled the keypress.  Foo.
			NoteUserSelection();
			ResetCommandPasting();
			if (iSearchMode) {
				DoISearch(e);
				e.Handled = true;
				return;
			}
			Unhighlight();
			if (userSelection.length>0 || e.KeyChar==' ' || e.KeyChar=='\r')
				MarkUndo();

			if (greekNext) {
				char newChar = ToGreek(e.KeyChar);
				SetRegionText(userSelection, new String(newChar,1));
				e.Handled = true;
				greekNext = false;
			} else
				greekNext = false;

			if (e.KeyChar==' ' && SpacePressed != null)
				SpacePressed();

			e.Handled = e.Handled || MaybeHandleCommandKey(e.KeyChar) || MaybeHandleCharacterSubstitution(e.KeyChar);
			
			UpdateTextStyles(e.KeyChar);
			RestoreUserSelection();
			if (!Char.IsControl(e.KeyChar) && WindowLocked)
				// Things feel sluggish if you don't see the char appear on the screen until
				// you let go of the key, so unlock the window right away if it's a printable character.
				UnlockWindow();
			AssertNotMultiplyLocked();
			return;
		}

		//
		// Check if key is a command and handle it.
		// Returns true if it's handled the event.
		//
		bool MaybeHandleCommandKey(char key) {
			// NB: Always returns true if this switch matches something.
			switch (key) {
					// Control-G: Greek mode
				case (char)7:
					greekNext=true;
					break;

					// Control-R: Ignore (triggers right justify mode, otherwise)
				case (char)18:
					break;

				case '\t':
					Complete();
					break;

				default:
					return false;
			}

			return true;
		}

		public void StartISearch() {
			iSearchMode=true;
			iSearchStartPoint=userSelection.start;
			iSearchString="";
			MaybeUnlockWindow();
			UpdateISearchStatus();
		}

		public void EndISearch() {
			if (iSearchMode)
				iSearchMode = false;
			SetStatusLine("");
		}

		public void ISearchNext() {
			NoteUserSelection();
			iSearchStartPoint = userSelection.start+iSearchString.Length;
			UpdateISearchStatus();
		}

		void UpdateISearchStatus() {
			if (iSearchString.Length==0) {
				userSelection=new Region(iSearchStartPoint,0);
				RestoreUserSelection();
				SetStatusLine("isearch (esc to quit): ");
			} else {
				int match = Find(iSearchString, iSearchStartPoint, RichTextBoxFinds.None);
				SetStatusLine("isearch (esc to quit): "+iSearchString+((match>=0)?"":"  not found!"));
			}
		}

		void DoISearch(KeyPressEventArgs e) {
			switch (e.KeyChar) {
				case (char)8:  // backspace
					if (iSearchString.Length>0)
						iSearchString = iSearchString.Substring(0, iSearchString.Length-1);
					UpdateISearchStatus();
					break;

				case (char)27:  // Escape
				case (char)13:  // Return
					EndISearch();
					break;

				default:
					iSearchString = iSearchString+e.KeyChar;
					UpdateISearchStatus();
					break;
			}
		}

		Keys currentModifiers;
		bool isDeleting = false;  // Used to track runs of backspaces for saving undo state.
		protected override void OnKeyDown(KeyEventArgs e) {
			currentModifiers = e.Modifiers;
			//This turns out to be a bad thing to check for, since it can happen in certain rollover situations with ESC.
			//Debug.Assert(!WindowLocked, "Window locked on entry to OnKeyDown");
			UnlockWindowDammit();

			// First check if it's the Isearch key
			if (iSearchMode) {
				switch (e.KeyCode) {
					case Keys.F3:
						ISearchNext();
						break;

					case Keys.Up:
					case Keys.Down:
					case Keys.Left:
					case Keys.Right:
						EndISearch();
						break;
				}
				e.Handled = true;
				AssertNotMultiplyLocked();
				return;
			} 
			//			else if (e.KeyCode == Keys.F3) {
			//				StartISearch();
			//				e.Handled =true;
			//				return;
			//			}

			// It turns out we need this to unhighlight before the base class keypress method gets called.
			// Putting it in OnKeyPress doesn't help because the low level COM object is sometimes
			// trapping the event before we get it.  Or at least that's what it seems like.
			if (e.KeyCode==Keys.LControlKey || e.KeyCode==Keys.RControlKey || e.KeyCode==Keys.ControlKey || e.KeyCode==Keys.ShiftKey || 
				e.KeyCode==Keys.LShiftKey || e.KeyCode==Keys.LShiftKey || e.KeyCode==Keys.Alt || e.KeyCode==Keys.Menu) {
				//AssertNotMultiplyLocked();
				//MaybeUnlockWindow();
				UnlockWindowDammit();
				return;
			}
			LockWindow();
			NoteUserSelection();
			Unhighlight();
#if SYNTAXHIGHLIGHTING
			Keys code = e.KeyCode;
			switch (e.KeyCode) {
				case Keys.Back:
				case Keys.Delete:
					if (EditorConfiguration.doComment)
						reComment =  IsTypingAfter("\u00ab") | IsTypingAfter("\u00bb")	| RegionText(userSelection).IndexOfAny(commentChars)>=0;
					break;
			}	
#endif
			// Mark Undo if necessary
			if (e.KeyCode==Keys.Back || e.KeyCode==Keys.Delete) {
				if (!isDeleting) {
					isDeleting=true;
					MarkUndo();
				}
			} else {
				if (isDeleting) {
					isDeleting=false;
					MarkUndo();
				}
			}

			switch (e.KeyCode) {
				case Keys.Up:
				case Keys.Down:
				case Keys.Left:
				case Keys.Right:
					MarkUndo();
					break;

				default:
					// Do nothing
					break;
			}
			RestoreUserSelection();
			if (!e.Control && (e.KeyCode==Keys.Up || e.KeyCode==Keys.Down
				|| e.KeyCode==Keys.Left || e.KeyCode==Keys.Right ||
				e.KeyCode==Keys.Back || e.KeyCode==Keys.Delete ||
				e.KeyCode==Keys.Space))

				UnlockWindowDammit();
			AssertNotMultiplyLocked();
		}

		protected override void OnMouseDown(MouseEventArgs e) {
			EndISearch();
			MarkUndo();
			NoteUserSelection();
			LockWindow();
			VerticalPositionChanged();
			RestoreUserSelection();
			MaybeHighlightExpression();
			UnlockWindowDammit();
#if DEBUGMOUSECLICK
			EditorWindow.SetArglistText(TopLevelExpressionRegion(userSelection.start).ToString());
#endif
		}

		protected override void OnMouseUp(MouseEventArgs e) {
			UnlockWindowDammit();
			base.OnMouseUp (e);
		}

		protected override void OnGotFocus(EventArgs e) {
			UnlockWindowDammit();
			base.OnGotFocus (e);
		}

        string toolTipCache = null;
		protected override void OnMouseMove(MouseEventArgs e) {
            if (tooltip.Active)
            {
                string tip = "";
                int index = GetCharIndexFromPosition(new Point(e.X, e.Y));
                if (index >= 0 && index < TextLength)
                {
                    switch (currentModifiers)
                    {
                        case Keys.Control | Keys.Shift:
                            // Display the expression object for the current mouse location.
                            Expression exp = EditorWindow.ExpressionAtIncludingVariable(index);
                            if (exp != null)
                                tip = Writer.SafeWriteToString(exp, 100);
                            //if (exp!=null && exp.InferredType!=null)
                            //	tip = exp.InferredType.ToString();
                            break;

                        case Keys.Control:
                            // Display the type for the expression the mouse is over.  Turns out not to be too useful,
                            // since the ExpressionAt can't locate variable references, and they're what you really want
                            // to find the types of.
                            Expression texp = EditorWindow.ExpressionAtIncludingVariable(index);

                            if (texp != null)
                            {
                                Type t = texp.InferredType;
                                if (t == null)
                                    t = texp.DeclaredType;
                                if (t != null)
                                {
                                    if (t is ITypeExpression)
                                        tip = t.ToString();
                                    else
                                        tip = t.Name;
                                }
                            }
                            break;

                        default:
                            // Display documentation for current symbol, if any
                            CodeScanner s = ScanCharsFrom(index);
                            int start = s.SkipBackwardToken();
                            if (start >= 0)
                            {
                                int end = s.SkipForwardToken();
                                if (end < TextLength)
                                {
                                    string maybeVar = RegionText(new Region(start, end - start));
                                    object b = Namespace.CheckBinding(maybeVar);
                                    if (b != null)
                                    {
                                        tip = Utilities.Documentation(b);
                                        if (tip == null)
                                            tip = b.ToString();
                                    }
                                }
                            }
                            break;
                    }
                }
                if (tip != toolTipCache)
                {
                    tooltip.SetToolTip(this, tip);
                    toolTipCache = tip;
                }
            }
			base.OnMouseMove (e);
		}


		static int suppressKeyUp=-1;
		public static void SuppressKeyUp(Keys c) {
			suppressKeyUp = (int)c;
		}

		// On entry to OnKeyUp, the window may still be locked, so we have to be sure to
		// unlock it before returning.
		protected override void OnKeyUp(KeyEventArgs e) {
			currentModifiers = e.Modifiers;
			if (iSearchMode) {
				e.Handled = true;
				// Search mode doesn't lock, so we don't need to unlock.
				return;
			}

			//if (!e.Control || (e.KeyCode!=Keys.Up && e.KeyCode != Keys.Down))
			//	ResetCommandPasting();

			if ((suppressKeyUp>=0 && (int)e.KeyCode==suppressKeyUp) || 
				// The Inspector can't tell us about this because it's in an assembly that can't import from us, so we hardcode here.
				(e.Control && (e.KeyCode==Keys.D1 || e.KeyCode==Keys.E || e.KeyCode==Keys.D))) {
				// Kluge: we get the keyup event with the user types control-d at the inspector
				// to pop up source code.  So we need to suppress processing of the keyup
				// to keep from turning off the highlighting.
				UnlockWindowDammit();
				return;
			}
			suppressKeyUp=-1;

			if (e.KeyCode==Keys.Menu || e.KeyCode ==Keys.ControlKey || e.KeyCode == Keys.ShiftKey) {
				// Don't mess with the screen if it's just the user pushing some modifier key.
				UnlockWindowDammit();
				return;
			}
			if (nextStyle != null) {
				SetRegionStyle(userSelection, nextStyle);
				nextStyle=null;
			}
			NoteUserSelection();
			Unhighlight();

#if SYNTAXHIGHLIGHT			
			if (EditorConfiguration.doHighlight) 
				HighlightUpdate((char) e.KeyValue, userSelection.start-1);
			if (EditorConfiguration.doComment& reComment)
				ReComment();
#endif

			switch (e.KeyCode) {
				case Keys.Down:
				case Keys.Enter:
				case Keys.Up:
					CodeScanner cs = ScanCharsFrom(userSelection.start);
					if (cs.AtBeginningOfLine && cs.CurrentChar==Meta.TopLevel.promptCarret && userSelection.length==0)
						// Skip over prompt
						userSelection.start += 2;

					VerticalPositionChanged();
					break;
			}

			switch (e.KeyCode) {
					//case Keys.OemCloseBrackets:
					//	// Cursor is now just after bracket, so start at backet
					//	HighlightExpressionBefore(userSelection.start);
					//	break;

				case Keys.Enter:
					// Nuke leading whitespace
					if (!e.Control) {
						int whitespace = ScanCharsFrom(userSelection.start).SkipForwardWhiteSpaceToEOL() - userSelection.start;
						if (whitespace>0)
							SetRegionText(new Region(userSelection.start, whitespace), "");
					}
					break;

				case Keys.Up:
				case Keys.Down:
				case Keys.Left:
				case Keys.Right:
				case Keys.Home:
				case Keys.End:
				case Keys.T:
					if (e.Control && e.Alt) {
						CodeScanner cs = ScanCharsFrom(userSelection.start);
						switch (e.KeyCode) {
							case Keys.Up:
								cs.Backward();
								cs.SkipBackwardToEnclosingExpression();
								break;

							case Keys.Left:
								cs.SkipBackwardExpression();
								break;

							case Keys.Right:
								cs.SkipForwardExpression();
								break;

							case Keys.Home:
								cs.Backward();
								cs.SkipBackwardToTopLevelExpression(blankLinesMarkTopLevelRegions);
								break;

							case Keys.End:
                                cs.SkipBackwardToTopLevelExpression(blankLinesMarkTopLevelRegions);
								cs.SkipForwardExpression();
								if (cs.charIndex==userSelection.Start)
									// We're back where we started, so go to the end of the next one
									cs.SkipForwardExpression();
								break;
#if WINFORMS1
                            // This is a menu command in WINFORMS2
							case Keys.T:
								HandleTranspose(null, null);
								break;
#endif
						}
						int boe = cs.charIndex;
						if (boe<userSelection.start) {
							// moved backward
							userSelection.length = 
								e.Shift?(userSelection.start+userSelection.length-boe):0;
							userSelection.start = boe;
						} else {
							// moved forward
							if (e.Shift)
								userSelection.End = boe;  // Extend selection right
							else {
								userSelection.length = 0;  // Just move the selection.
								userSelection.start = boe;
							}
						}
						VerticalPositionChanged();
						ScrollToCaret();
						e.Handled = true;
					}
#if WINFORMS1
                    // This is handled by a menu keybinding in WinForms 2.0
                    else if (e.Control) {
						switch (e.KeyCode) {
							case Keys.Up:
								PastePreviousCommand();
								break;

							case Keys.Down:
								PasteNextCommand();
								break;
						}
					}
#endif
                    else if (e.KeyCode == Keys.Home) {
						if (RegionText(new Region(userSelection.start, TopLevel.promptString.Length))==TopLevel.promptString)
							userSelection = new Region(userSelection.start+TopLevel.promptString.Length, 0);
					}
					break;

#if WINFORMS1
                // Handled by a menu shortvut in WinForms 2
				case Keys.Oemcomma:
					if (e.Control && e.Shift)
						FormatShrinkClick(null, null);
					break;
				case Keys.OemPeriod:
					if (e.Control && e.Shift)
						FormatGrowClick(null, null);
					break;

				case Keys.Oemplus:
					if (e.Control) {
						if (e.Shift)
							FormatSuperscriptClick(null, null);
						else
							FormatSubscriptClick(null, null);
					}
					break;
#endif
			}

			// Just for testing CurrentX properties
			//((Editor)FindForm()).SetStatusBarText(CurrentDefinitionVariable==null?"<null>":("'"+CurrentDefinitionVariable+"'"));

			if (e.KeyCode==Keys.Enter && e.Control  && EditorWindow is Listener) {
				// Sick kluge.  There's no good way to keep the RichTextBox from inserting a newline
				// when we type this.  That's okay for code buffers, but bad for the listener because the
				// eval code will print another newline.  So we delete the newline.  Eeeew...
				// This turns out to be the cause of the desktop redrawing/refreshing itself when
				// you do control-enter.  Don't know why.
				//SetRegionText(new Region(userSelection.start-1, 1), "");
				((Listener)EditorWindow).Out.SuppressNewLine();
			}

			RestoreUserSelection();
			MaybeHighlightExpression();
			UnlockWindowDammit();
			if (e.KeyCode==Keys.Enter && e.Control) {
				HandleEval(this, null);
			}
			return;
		}

		public void HandleSelectAll(object sender, EventArgs e) {
			SelectAll();
		}
		public void HandleCut(object sender, EventArgs e) {
			MarkUndo();
			Clipboard.SetDataObject(RegionText(userSelection));
			Cut();

#if SYNTAXHIGHLIGHTING
			if (EditorConfiguration.doComment)
			{
				NoteUserSelection();
				ReComment();
				RestoreUserSelection();
			}
#endif

		}
		public void HandleCopy(object sender, EventArgs e) {
			Unhighlight();
			Clipboard.SetDataObject(RegionText(userSelection));
			Copy();
		}
		public void HandlePaste(object sender, EventArgs e) {
			Unhighlight();
			NoteUserSelection();
			MarkUndo();
			Paste();
			NoteUserSelection();
			IndentTopLevelExpression();

#if SYNTAXHIGHLIGHTING
			if (EditorConfiguration.doComment)
			{
				NoteUserSelection();
				ReComment();
				RestoreUserSelection();
			}
#endif
			RestoreUserSelection();
		}

		int pastedCommandIndex = -1;
		public Region pastedCommandRegion;
		public void ResetCommandPasting() {
			pastedCommandIndex=-1;
		}

		public static bool debuggingCommandPaste = true;
		public void PastePreviousCommand() {
			Unhighlight();
			NoteUserSelection();
			MarkUndo();
			if (Listener.previousCommandLengths.Count==0)
				return; // Nothing to paste!

			if (pastedCommandIndex!=0) { // not on the last command
				if (pastedCommandIndex<0) {
					// Haven't started commands yet
					MarkUndo();
					pastedCommandIndex = Listener.previousCommands.Count-1;
					// KLUGE!  SelectionStart sometimes reads as 2 less that the actual location of the insertion point.
					//if (userSelection.start == TextLength-2 && userSelection.length == 0)
					//	userSelection.start += 2;

					//pastedCommandRegion = userSelection;

					// Sometimes, but not always, the richeditcontrol gets the control-up key and moves the cursor before we run.  Argh!
					pastedCommandRegion = new Region(TextLength, 0);
				} else
					// Go to the next command
					pastedCommandIndex--;

				int kluge = pastedCommandIndex;
				string newRTF = (string)Listener.previousCommands[pastedCommandIndex];
				if (pastedCommandRegion.End>TextLength)
					pastedCommandRegion.End = TextLength;
				SetRegionRTF(pastedCommandRegion, newRTF);  // this is very disturbing; according to dev studio, this line mutates pastedCommandIndex...
				kluge = Math.Min(kluge, Listener.previousCommandLengths.Count-1);
				pastedCommandIndex = kluge;
				pastedCommandRegion.length = (int)Listener.previousCommandLengths[kluge];
				if (pastedCommandRegion.End>TextLength)
					pastedCommandRegion.End = TextLength;
				userSelection.start=pastedCommandRegion.End;
				userSelection.length=0;
				IndentTopLevelExpression();
			}
			RestoreUserSelection();
		}

		public void PasteNextCommand() {
			Unhighlight();
			NoteUserSelection();

			if (pastedCommandIndex<Listener.previousCommands.Count-1) {
				pastedCommandIndex++;

				int kluge = pastedCommandIndex;
				string newRTF = (string)Listener.previousCommands[pastedCommandIndex];
				if (pastedCommandRegion.End>TextLength)
					pastedCommandRegion.End = TextLength;
				SetRegionRTF(pastedCommandRegion, newRTF);  // this is very disturbing; according to dev studio, this line mutates pastedCommandIndex...
				pastedCommandIndex = kluge;
				pastedCommandRegion.length = (int)Listener.previousCommandLengths[kluge];
				if (pastedCommandRegion.End>TextLength)
					pastedCommandRegion.End = TextLength;
				userSelection.start=pastedCommandRegion.End;
				userSelection.length=0;
				IndentTopLevelExpression();
			}
			RestoreUserSelection();
		}


		public void HandleTranspose(object sender, EventArgs e) {
			NoteUserSelection();
			MarkUndo();
			
			CodeScanner sc = ScanCharsFrom(userSelection.start);
			// Cursor is now either at the beginning of the right exp or in the whitespace between
			sc.SkipBackwardToWhiteSpace();
			// Cursor is now definitely in the whitespace
			sc.SkipBackwardWhiteSpace();
			// Cursor now on the last char of the left exp
			sc.Forward();
			// Cursor now on the first char of whitespace between
			int leftExpEnd = sc.charIndex;
			int leftExpStart = sc.SkipBackwardExpression();
			// Cursor now on the first char of left
			int leftExpLength = leftExpEnd-leftExpStart;

			sc.SkipForwardExpression();
			// Back to last char of left
			int rightExpStart = sc.SkipForwardWhiteSpace();
			// First char of right
			int rightExpEnd = sc.SkipForwardExpression();
			// Last char of right
			int rightExpLength = rightExpEnd-rightExpStart;

			Region left = new Region(leftExpStart, leftExpLength);
			Region right = new Region(rightExpStart, rightExpLength);
			string leftExpSaved = RegionRTF(left);

			SetRegionRTF(left, RegionRTF(right));
			int leftSizeChange = rightExpLength-leftExpLength;
			right.start += leftSizeChange;
			left.length = rightExpLength;
			SetRegionRTF(right, leftExpSaved);

			IndentTopLevelExpression();

			userSelection.start = left.End;
			userSelection.length = 0;
			RestoreUserSelection();
		}


		FontDialog fontDialog = new FontDialog();

		public void HandleTextChanged(object sender, EventArgs e) {
			if (!Disposing) {
				Modified = true;
				ModifiedSinceUndoMark = true;
				reIndent = true;
				reindentTimer.Stop();
				reindentTimer.Start();
			}
		}

		public void HandleFont(object sender, EventArgs e) {
			NoteUserSelection();
			MarkUndo();
			fontDialog.ShowColor = true;
			fontDialog.Font = SelectionFont;
			fontDialog.Color = SelectionColor;
			if(fontDialog.ShowDialog() != DialogResult.Cancel ) {
				SetRegionFont(userSelection, fontDialog.Font);
				SetRegionColor(userSelection, fontDialog.Color);
			}
		}

		public void HandleUndo(object sender, EventArgs e) {
			MyUndo();
		}

		public void HandleEval(object sender, EventArgs e) {
			HandleEvalInternal(false, false);
		}

		public void HandleStep(object sender, EventArgs e) {
			HandleEvalInternal(true, false);
		}

		public void HandleInspect(object sender, EventArgs e) {
			HandleEvalInternal(false, true);
		}

		private void HandleEvalInternal(bool stepMode, bool inspectMode) {
			LockWindow();
			ScrollPoint s = ScrollPosition;
			NoteUserSelection();
#if EvalRegions
			Region r=userSelection.length>0?userSelection:
				TopLevelExpressionRegion(userSelection.start);
#else
			userSelection.length=0;
			Region r = TopLevelExpressionRegion(userSelection.start);
#endif
			System.Diagnostics.Debug.Assert(!RegionText(r).StartsWith(" "));
			Listener.previousCommands.Add(RegionRTF(r));
			string text = RegionText(r);
			RestoreUserSelection();
			ScrollPosition = s;
			UnlockWindow();
			if (text.IndexOf(Meta.TopLevel.promptCarret)>=0) {
				System.Windows.Forms.MessageBox.Show("Please place the carret at the end of the expression and try again", "Can't find the expression", MessageBoxButtons.OK);
			} else {
				Listener.previousCommandLengths.Add(text.Length);
				Editor parent = EditorWindow;
				if (parent!=null && parent is Listener)
					((Listener)parent).EvalString(text+' ', stepMode, inspectMode, parent.fileToken, r.start, true);
				else
					Listener.mainListener.EvalString(text+' ', stepMode, inspectMode, parent.fileToken, r.start, true);
			}
		}

		public void HandleEvalFile(object sender, EventArgs e) {
			Meta.Reader reader = new Meta.Reader(new System.IO.StringReader(Text), 0);
			Listener.mainListener.LoadStream(reader, EditorWindow.fileToken);
		}

		public void HandleEvalRegion(object sender, EventArgs e) {
			NoteUserSelection();
			Meta.Reader reader = new Meta.Reader(new System.IO.StringReader(RegionText(userSelection)), userSelection.start);
			Listener.mainListener.LoadStream(reader, EditorWindow.fileToken);
		}

		public void HandleLoadFile(object sender, EventArgs e) {
			Stream myStream;
			OpenFileDialog openFileDialog1 = new OpenFileDialog();

			//openFileDialog1.InitialDirectory = "c:\\" ;
			openFileDialog1.Filter = "Source and object files (*.meta; *.dll)|*.meta;*.dll|.Net assemblies (*.dll)|*.dll|All files (*.*)|*.*" ;
			openFileDialog1.FilterIndex = 1 ;
			openFileDialog1.RestoreDirectory = true ;
			if (EditorWindow.fileToken.Path != null)
				openFileDialog1.InitialDirectory = Path.GetDirectoryName(EditorWindow.fileToken.Path);

			if(openFileDialog1.ShowDialog() == DialogResult.OK) {
				if (Path.GetExtension(openFileDialog1.FileName).ToLower()==".dll") {
					try {
						Loader.Load(openFileDialog1.FileName);
					} catch (Exception ex) {
						MessageBox.Show(ex.Message, "Error loading DLL", System.Windows.Forms.MessageBoxButtons.OK, MessageBoxIcon.Error);
					}
				} else if((myStream = openFileDialog1.OpenFile())!= null) {
					// Insert code to read the stream here.
					Meta.Reader reader = new Reader(new RTFTextReader(new StreamReader(myStream)), 0);
					Listener.mainListener.LoadStream(reader, FileToken.GetToken(openFileDialog1.FileName));
				} else MessageBox.Show("Can't open the file "+openFileDialog1.FileName, "Error", System.Windows.Forms.MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		protected override void OnDragEnter(DragEventArgs drgevent) {
			if (drgevent.Data.GetDataPresent(DataFormats.FileDrop))
				drgevent.Effect = DragDropEffects.Copy;
			else
				base.OnDragEnter (drgevent);
		}

		protected override void OnDragDrop(DragEventArgs drgevent) {
			object[] data = (object[])drgevent.Data.GetData(DataFormats.FileDrop);
			string[] names = new string[data.Length];
			data.CopyTo(names,0);
			Listener.ProcessCommandLineArgs(names);
			//base.OnDragDrop (drgevent);
		}


		public void HandleStop(object sender, EventArgs e) {
			//			Listener.mainListener.SetAllCursors(Cursors.IBeam);
			if (EditorWindow is Listener)
				((Listener)EditorWindow).KillEvalThread();
			else
				Listener.mainListener.KillEvalThread();
		}

		public void FormatMenuPop(ToolStripMenuItem formatBold, ToolStripMenuItem formatItalic, ToolStripMenuItem formatBullet) {
			Unhighlight();
			NoteUserSelection();
			System.Drawing.Font f = RegionFont(userSelection);

			if (f!=null) {
				formatBold.Checked = f.Bold;
				formatItalic.Checked = f.Italic;
			}
			formatBullet.Checked = RegionBullet(userSelection);
		}

		public void FormatBoldClick(object sender, EventArgs e) {
			Unhighlight();
			NoteUserSelection();
			MarkUndo();
			System.Drawing.Font f = RegionFont(new Region(userSelection.start, 0));
			SetRegionFont(userSelection, new System.Drawing.Font(f, f.Style^FontStyle.Bold));
		}
		public void FormatItalicClick(object sender, EventArgs e) {
			Unhighlight();
			NoteUserSelection();
			MarkUndo();
			System.Drawing.Font f = RegionFont(userSelection);
			SetRegionFont(userSelection, new System.Drawing.Font(f, f.Style^FontStyle.Italic));
		}
		public void FormatGrowClick(object sender, EventArgs e) {
			Unhighlight();
			NoteUserSelection();
			MarkUndo();
			System.Drawing.Font f = RegionFont(userSelection);
			SetRegionFont(userSelection, new System.Drawing.Font(f.FontFamily, f.Size + 2.0F));
		}
		public void FormatShrinkClick(object sender, EventArgs e) {
			Unhighlight();
			NoteUserSelection();
			MarkUndo();
			System.Drawing.Font f = RegionFont(userSelection);
			SetRegionFont(userSelection, new System.Drawing.Font(f.FontFamily, f.Size - 2.0F));
		}
		public void FormatSuperscriptClick(object sender, EventArgs e) {
			Unhighlight();
			MarkUndo();
			NoteUserSelection();
			SetRegionCharOffset(userSelection, RegionCharOffset(userSelection)+2);
		}
		public void FormatSubscriptClick(object sender, EventArgs e) {
			Unhighlight();
			MarkUndo();
			NoteUserSelection();
			SetRegionCharOffset(userSelection, RegionCharOffset(userSelection)-2);
		}
		public void FormatBulletClick(object sender, EventArgs e) {
			Unhighlight();
			MarkUndo();
			NoteUserSelection();
			SetRegionBullet(userSelection, !RegionBullet(userSelection));
		}

		public void HandleStyle(object sender, System.EventArgs e) {
			string styleName = ((MenuItem)sender).Text.Replace("&", "");
			Style style = TheStyle(styleName);

			NoteUserSelection();
			MarkUndo();
			SetRegionStyle(userSelection, style);
			RestoreUserSelection();
		}

		public void ShowReferences(string name) {
			Listener.mainListener.EvalString("[Packages.ShowReferences.show-references '"+name+"]", false, false, EditorWindow.fileToken, 0, false);
		}

		public void HandleShowReferences(bool showDefinitionRefs) {
			string var = showDefinitionRefs?CurrentDefinitionVariable:CurrentVariableName;
			if (var!=null)
				ShowReferences(var);
		}

		//
		// Parsing of user's current selection
		//

		// Just the variable name part of the current token without any ".memberName"
		public string CurrentVariableName {
			get {
				return Utilities.StripMemberReferences(CurrentVariableNameOrMemberReference);
			}
		}

		// The full current token, including member references
		public string CurrentVariableNameOrMemberReference {
			get {
				// yuk
				if (userSelection.start==TextLength || (userSelection.start>=0 && userSelection.start<TextLength && char.IsWhiteSpace(Text[userSelection.start])))
					return RegionText(CurrentWord(userSelection.start-1));
				return RegionText(CurrentWord(userSelection.start));
			}
		}

		public Expression CurrentVariableExpression {
			get {
				Symbol s = Symbol.MaybeIntern(CurrentVariableName);
				if (s!=null)
					return this.Namespace.Lookup(s, true, false, true);
				else
					return null;
			}
		}

		public string CurrentExpressionOperator {
			get {
				// Find the enclosing expression
				CodeScanner cs = ScanCharsFrom(Math.Min(userSelection.start, TextLength));
				cs.SkipBackwardToEnclosingExpression();
				if (cs.AtBeginningOfBuffer | cs.AtEndOfBuffer)
					return null;

				// Move forward over [
				cs.Forward();
				int operatorStart = cs.charIndex;
				if (cs.AtBeginningOfBuffer | cs.AtEndOfBuffer)
					return null;

				// Find the end of the variable name inside
				int operatorEnd = cs.SkipForwardToken();  // Find the end of this expression
				//if (cs.AtBeginningOfBuffer | cs.AtEndOfBuffer)
				//	return null;

				// Return the text
				return RegionText(new Region(operatorStart, operatorEnd-operatorStart));
			}
		}

		public string CurrentTopLevelExpression {
			get {
				// Find the start of the top-level expression
				CodeScanner cs = ScanCharsFrom(userSelection.start-1);
                cs.SkipBackwardToTopLevelExpression(blankLinesMarkTopLevelRegions);
				if (cs.AtBeginningOfBuffer | cs.AtEndOfBuffer)
					return null;
				int expStart = cs.charIndex;

				// Find the end of the expression
				int expEnd = cs.SkipForwardExpression();  // Find the end of this expression
				if (cs.AtBeginningOfBuffer | cs.AtEndOfBuffer)
					return null;

				// Return the text
				return RegionText(new Region(expStart, expEnd-expStart));
			}
		}	

		public string CurrentTopLevelExpressionOperator {
			get {
				// Find the start of the top-level expression
				CodeScanner cs = ScanCharsFrom(userSelection.start-1);
                cs.SkipBackwardToTopLevelExpression(blankLinesMarkTopLevelRegions);
				if (cs.AtBeginningOfBuffer | cs.AtEndOfBuffer)
					return null;

				// Move forward over [
				cs.Forward();
				int operatorStart = cs.charIndex;
				if (cs.AtBeginningOfBuffer | cs.AtEndOfBuffer)
					return null;

				// Find the end of the variable name inside
				int operatorEnd = cs.SkipForwardToken();  // Find the end of this expression
				if (cs.AtBeginningOfBuffer | cs.AtEndOfBuffer)
					return null;

				// Return the text
				return RegionText(new Region(operatorStart, operatorEnd-operatorStart));
			}
		}

		public int CurrentDefinitionStart {
			get {
				return StartOfDefinitionAt(userSelection.start-1);
			}
		}

		public int StartOfDefinitionAt(int index) {
			try {
				// Find the start of the top-level expression
				CodeScanner cs = ScanCharsFrom(index);
                cs.SkipBackwardToTopLevelExpression(blankLinesMarkTopLevelRegions);
				if (cs.AtBeginningOfBuffer | cs.AtEndOfBuffer)
					return -1;
				else return cs.charIndex;
			} catch {
				return -1;
			}
		}

		public string CurrentDefinitionVariable {
			get {
				return DefinitionNameAt(userSelection.start-1);
			}
		}

		public string DefinitionNameAt(int index) {
			try {
				// Find the start of the top-level expression
				CodeScanner cs = ScanCharsFrom(index);
                cs.SkipBackwardToTopLevelExpression(blankLinesMarkTopLevelRegions);
				if (cs.AtBeginningOfBuffer | cs.AtEndOfBuffer)
					return null;

				// Move forward over [
				cs.Forward();
				int operatorStart = cs.charIndex;
				if (cs.AtBeginningOfBuffer | cs.AtEndOfBuffer)
					return null;

				// Find the end of the variable name inside
				int operatorEnd = cs.SkipForwardToken();  // Find the end of this expression
				if (cs.AtBeginningOfBuffer | cs.AtEndOfBuffer)
					return null;

				// Check it's a definition
				if (RegionText(new Region(operatorStart, Math.Min(3,operatorEnd-operatorStart))) != "def")
					return null;

				// Find the variable name
				cs.SkipForwardWhiteSpace();
				while (char.GetUnicodeCategory(cs.CurrentChar)==UnicodeCategory.OpenPunctuation)
					cs.Forward();
				// Here we are
				int varStart = cs.charIndex;
				if (cs.AtBeginningOfBuffer | cs.AtEndOfBuffer)
					return null;

				int varEnd = cs.SkipForwardToken();
				if (cs.AtBeginningOfBuffer | cs.AtEndOfBuffer)
					return null;

				// Return the text
				return RegionText(new Region(varStart, varEnd-varStart));
			} catch {
				return null;
			}

		}
		//
		// CONTEXT SENSITIVE INPUT PROCESSING
		//

		private bool IsTypingAfter(string text) {
			return text==RegionText(new Region(userSelection.start-text.Length, text.Length));
		}

		private bool MaybeChangeChar(string oldText, string newText) {
			Region charsBefore
				= new Region(userSelection.start-oldText.Length,oldText.Length);
			if (RegionText(charsBefore)==oldText) {
				char theChar = newText[0];
				object matching = CodeScanner.matchingBracket[theChar];

				if (Editor.insertMatchingBrackets && newText.Length==1
					&& (char.GetUnicodeCategory(theChar)==UnicodeCategory.OpenPunctuation ||
					char.GetUnicodeCategory(theChar)==UnicodeCategory.InitialQuotePunctuation)
					&& matching != null)
					SetRegionText(charsBefore, newText+new String((char)matching, 1));
				else
					SetRegionText(charsBefore, newText);
				return true;
			}
			return false;
		}
		private bool MaybeInsertOperator(string newText) {
			string TextBefore = RegionText(new Region(userSelection.start-1,1));
			if (TextBefore==" " || TextBefore=="[") {
				SetRegionText(userSelection, newText);
				return true;
			}
			return false;
		}

		//
		// SCANNING OPERATIONS
		//

		public CodeScanner ScanCharsFrom(int charIndex) {
			return new CodeScanner(Math.Min(charIndex, TextLength), Lines);
		}

		public Region ExpressionRegionBefore(int pos) {
			CodeScanner sc = ScanCharsFrom(pos);
			int start=sc.SkipBackwardExpression(false);
			if (sc.AtBeginningOfBuffer)
				return new Region(pos, 0);
			else
				return new Region(start, pos-start);
		}

		public Region ExpressionRegionAfter(int pos) {
			CodeScanner sc = ScanCharsFrom(pos);
			int end=sc.SkipForwardExpression();
			if (sc.AtEndOfBuffer)
				return new Region(pos, 0);
			else
				return new Region(pos, end-pos);
		}

		public Region TopLevelExpressionRegion(int pos) {
			CodeScanner s=ScanCharsFrom(pos);
            int start = s.SkipBackwardToTopLevelExpression(blankLinesMarkTopLevelRegions);
			int end=s.SkipForwardExpression();

			return new Region(start, end-start);
		}

		//want Lines to just be current line
		Region CurrentWord(int position) {
			CodeScanner GetWord = ScanCharsFrom(position);
			int start = CurrentWordStart(GetWord);
			CodeScanner GetWord2 = ScanCharsFrom(position);
			int end = CurrentWordEnd(GetWord2);
			if (start < end) 
				return new Region(start, (end-start)+1);
			else
				return new Region(0,0);
		}

		//i really only need the current line for this - unless the delims change
		//to where newline isn't necessarily a delim
		int CurrentWordStart(CodeScanner cs) {
			int start;
			if (cs.AtEndOfBuffer || Char.GetUnicodeCategory(cs.CurrentChar)==UnicodeCategory.ClosePunctuation)
				cs.Backward();
			if ((start = cs.SkipBackwardToWordSeparator()) < 0)
				return 0;
			else
				return ++start;
		}

		//i really only need the current line for this
		//need to rewrite this with better error handling
		int CurrentWordEnd(CodeScanner cs) {
			int end;
			if ((end = cs.SkipForwardToWordSeparator()) < 0)
				end = TextLength+1;
			return --end;
		}

		//get's the word to the left of the current delim position
		Region WordToLeft(int position) {
			int start;
			//if the delim is at the first char in the buffer, there can't be
			//any words to it's left
			if (position == 0)
				start = 0;
			else {
				CodeScanner GetLeftWord = ScanCharsFrom(position-1);
				start = CurrentWordStart(GetLeftWord);
			}
			return new Region(start, position-start);
		}

		//get's the word to the right of the current delim position
		Region WordToRight(int position) {
			//if the delim is the last char in the window, it can't have any words
			//to it's right
			if (position == TextLength-1)
				return new Region(0, 0);
			else {
				CodeScanner GetRightWord = ScanCharsFrom(position+1);
				return new Region(position+1, CurrentWordEnd(GetRightWord)-(position));
			}
		}

		//
		// INDENTATION
		//

#if OLDREINDENT
		private void ReindentCurrentLine() {
			IndentLine(userSelection.start);
		}

		public void IndentLine(int pos) {
			// Basic idea:
			// Skip backward to the second subexpression of this expression,
			// get its position and indent the current selection to that position.

			int indentPoint=0;    // buffer index of the beginning of the second expression
			int firstSubStart;      // char character and line number of the first subexpression of the enclosing expression.
			int firstSubLineNo;

			CodeScanner s = ScanCharsFrom(pos);
			s.SkipBackwardToBOL();
			if (s.CurrentChar!=TopLevel.promptCarret) {
				if (s.MaybeSkipBackwardToEnclosingExpression() && Char.GetUnicodeCategory(s.CurrentChar)==UnicodeCategory.OpenPunctuation) {
					// Skip over the [
					s.Forward();
					// Check what kind of expression it is
					object indentStyle = null;
					if (Editor.indentDefsLispStyle && RegionText(new Region(s.charIndex, 3))=="def")
						indentStyle = Symbol.sBody;
					else {
						string t = Text;  // Cheat to quickly find symbol name
						int symEnd = t.IndexOf(' ',s.charIndex);

						if (symEnd>0) {
							string symName = t.Substring(s.charIndex, symEnd-s.charIndex);
							Meta.Parser maybeParser = Meta.Namespace.CurrentNamespace.CheckBinding(symName) as Meta.Parser;

							if (maybeParser!= null)
								indentStyle = maybeParser.IndentStyle;
						}
					}

					if (indentStyle==Symbol.sBody) {
						// It ends in a body; Indent two chars in.
						indentPoint = s.charIndex+2;
					} else if (indentStyle==Symbol.sWith) {
						int withStart = s.charIndex;
						// Skip over with
						s.SkipForwardExpression();
						s.SkipForwardWhiteSpace();
						indentPoint = s.charIndex;  // Assume we'll be intendint to the level of the second sexp
						// Now skip over the = clauses
						while (s.charIndex < pos) {
							// Skip the variable name
							s.SkipForwardExpression();
							// Is there an = here?
							s.SkipForwardWhiteSpace();
							if (s.CurrentChar=='=') {
								// Skip it
								s.SkipForwardExpression();
								// And now skip the value
								s.SkipForwardWhiteSpace();
								s.SkipForwardExpression();
							} else if (s.CurrentChar==':') {
								// Oops, this is a named let.  The next expresion is actually the indent point.
								s.SkipForwardExpression();
								s.SkipForwardWhiteSpace();
								indentPoint = s.charIndex;
							} else {
								// Okay, the code we're trying to indent comes *after* the end of the = clauses
								// so we *don't* indent it to the level of the second sexp; instead, we indent to
								// the 't' in "with".
								indentPoint = withStart+2;
								break;
							}
						}
					} else if (char.GetUnicodeCategory(s.CurrentChar)==UnicodeCategory.OpenPunctuation) {
						indentPoint = s.charIndex;
					} else {
						// Find the beginning of the second subexpression of the enclosing expression.
						firstSubStart = s.charIndex;
						firstSubLineNo = s.lineNumber;
						s.SkipForwardExpression();
						s.SkipForwardWhiteSpace();
						if (s.lineNumber == firstSubLineNo) {
							if (s.CurrentChar == ':')
								// It's a function definition, indent the body two chars in from the head.
								indentPoint = firstSubStart+2;
							else {
								// The second subexpression is on the same line as the first, so we indent to it
								indentPoint = s.charIndex;
								// But first check to make sure this isn't the arglist of a function...
								while (!s.AtEndOfBuffer && s.lineNumber == firstSubLineNo) {
									char c = s.CurrentChar;
									if (c == ']')
										// Oops, we're confused.
										break;
									if (c == '\u2192') {
										// Oops, this is a function definition; indent to to head plus two chars.
										indentPoint = firstSubStart+2;
										break;
									}
									s.SkipForwardExpression();
									s.SkipForwardWhiteSpace();
								}
								// We got to the next line safely, so it must not be a function definition.
								// So leave the indent point at the second subexpression.
							}
						} else
							// There's only one subexpression on this line, so indent to it.
							indentPoint = firstSubStart;
					}
				}
			}
			// else punt as use 0 for the indentation...

			// The only way to get the current indent of a position is to ask for its screen location
			int screenPosition = GetPositionFromCharIndex(indentPoint).X;
			// But then you have to subtract off the indent of something "unindented", especially
			// since the screen position changes when you scroll horizontally.
			int scrollPosition = GetPositionFromCharIndex(0).X;

			SetRegionIndent(new Region(pos,0),
				indentPoint>0?(screenPosition-scrollPosition):0);
		}
#endif

		void ReindentHandler(object sender, EventArgs e) {
			NoteUserSelection();
			IndentTopLevelExpression();
			RestoreUserSelection();
		}

		public void FinishReindentBeforeSave() {
			if (reindentTimer.Enabled) {
				reindentTimer.Stop();
				ReindentHandler(null, null);
			}
			MarkUndo();
		}

		public void IndentTopLevelExpression() {
			if (reIndent) {
#if PROFILEINDENT
			long startTicks = System.Diagnostics.Process.GetCurrentProcess().UserProcessorTime.Ticks;
#endif
				LockWindow();
				ScrollPoint savedScroll = ScrollPosition;
				Region top = TopLevelExpressionRegion(userSelection.Start);
				if (!currentTopLevelRegion.Equals(top) || reIndent) {
					currentTopLevelRegion = top;
					SetRegionIndent(new Region(top.start, 0), 0);  // Force the start of the TLE to the left margin
#if OLDREINDENT
				CodeScanner cs = ScanCharsFrom(top.start);
			
				do {
					IndentLine(cs.charIndex);
					cs.SkipForwardToBOL();
				} while (cs.charIndex<top.End && !cs.AtEndOfBuffer && !cs.IsBlankLine && !cs.IsDefinitionLine);
				IndentLine(userSelection.Start);  // just in case
#else
					//				new Indenter(this, top.start).Read();     // for debugging, so we can see what error is dropping it out.
					if (top.length>1)
						Formatter.FormatExpression(this, top.start, false, false, false);
#endif
				}
				RestoreUserSelection();  // Otherwise the scroll position will just immediately set itself back :-(
				ScrollPosition = savedScroll;
				UnlockWindow();
				reIndent = false;
#if PROFILEINDENT
			long elapsedTicks = System.Diagnostics.Process.GetCurrentProcess().UserProcessorTime.Ticks-startTicks;
			double time = ((double)elapsedTicks)/System.TimeSpan.TicksPerSecond;
			((Editor)FindForm()).SetArglistText(time.ToString()+" sec");
#endif
			}
			reindentTimer.Stop();
		}

		public void RemoveLeadingWhitespace() {
			int position=0;
			int line=0;
			bool firsttime=true;

			Editor e = EditorWindow;
			NoteUserSelection();

			while (true) {
				e.SetArglistText("Line "+line.ToString());
				line++;
				if (position>=TextLength) {
					userSelection=new Region(0,0);
					RestoreUserSelection();
					return;
				}
				CodeScanner s=ScanCharsFrom(position);
				if (!firsttime) {
					s.SkipForwardToBOL();
					position = s.charIndex;
				} else
					firsttime = false;
				if (char.IsWhiteSpace(s.CurrentChar)) {
					int whitespace = s.SkipForwardWhiteSpaceToEOL() - position;
					if (whitespace>0)
						SetRegionText(new Region(position, whitespace), "");
				}
			}
		}

		public void PrettyPrintKluge() {
			CodeScanner sc = ScanCharsFrom(TextLength);
			sc.SkipBackwardExpression(); // Skip over the Carret.
			sc.SkipBackwardExpression(); // Move to the start of the expression we want to post-pretty-print
			PrettyPrintExp(sc, GetPositionFromCharIndex(0).X+Width-20);
			SetRegion(new Region(TextLength, 0));
		}

		void PrettyPrintExp(CodeScanner sc, int bufferRight) {
			// First check if there's anything to do
			if (sc.CurrentChar != '[')
				// It's atomic
				return;
			CodeScanner cpy = new CodeScanner(sc);
			cpy.SkipForwardExpression();
			if (cpy.lineNumber == sc.lineNumber && GetPositionFromCharIndex(cpy.charIndex).X < bufferRight)
				// Not atomic, but fits on the line
				return;

			// Not atomic and doesn't fit on the line.

			int subexpressionIndex=0;
			sc.Forward();   // Skip the [
			sc.SkipForwardWhiteSpace();
			while (!sc.AtEndOfBuffer && sc.CurrentChar!=']') {
				sc.SkipForwardWhiteSpace();
				// Look ahead to see if it will fit on this line.
				CodeScanner sub = new CodeScanner(sc);
				sub.SkipForwardExpression();
				if (GetPositionFromCharIndex(sub.charIndex).X>=bufferRight) {
					// It doesn't fit.  Start a new line for it.
					if (subexpressionIndex>0)
						// We can only assume there's a space char here to turn into a cr
						// if it isn't the first subexpression.  Fortunately, we won't want
						// to break the first SE into a separate line anyway.
						SetRegionText(new Region(sc.charIndex-1, 1), "\n");
#if OLDINDENTER
					IndentLine(Math.Min(TextLength,sc.charIndex));
#endif

					// Check if it still overflows
					if (GetPositionFromCharIndex(sub.charIndex).X>=bufferRight)
						// Break it up, if possible
						PrettyPrintExp(new CodeScanner(sc), bufferRight);
					// Next subexpression
				}
				sc = sub;
				subexpressionIndex++;
			}
		}


		//
		// REGION OPERATIONS
		// The underlying RichTextBox only supports a single selection that you
		// access by setting SelectionStart, etc.  Regions let you pretend you
		// can have multiple simultaneous selections.  They also let you treat
		// selections as first-class data objects
		//

		private Region userSelection;
        public Region UserSelection
        {
            get
            {
                return userSelection;
            }
            set
            {
                userSelection = value;
            }
        }

		public void NoteUserSelection() {
#if HIGHLIGHTBYCOLORING
			userSelection.start = SelectionStart;
			userSelection.length = SelectionLength;
			currentRegion = userSelection;
#else
			if (highlightedRegion.length==0 ||  // nothing highlighted
				SelectionStart !=highlightedRegion.start || SelectionLength != highlightedRegion.length  // cursor got moved from highlighting
				) {
				userSelection.start = SelectionStart;
				userSelection.length = SelectionLength;
				currentRegion = userSelection;
			}
#endif
		}
		public void RestoreUserSelection() {
			SetRegion(userSelection);
		}

		private Region highlightedRegion;
#if HIGHLIGHTBYCOLORING
		private string highlightedRegionOldRTF = null;
#endif

		public void Unhighlight(object sender, EventArgs ignore) {
			LockWindow();
			ScrollPoint s = ScrollPosition;
			Unhighlight();
			// Have to do this because RestoreUserSelection sets Start before Length,
			// so the region can run of the end of the screen, forcing a scroll.
			SelectionLength = 0;
			RestoreUserSelection();
			ScrollPosition = s;
			UnlockWindow();
		}

		bool IsHighlighted {
			get {
				return highlightedRegion.length>0;
			}
		}

		public void Unhighlight() {
#if HIGHLIGHTBYCOLORING
			if (highlightedRegion.length>0) {
				//SetRegionColor(highlightedRegion, ForeColor);
				SetRegion(highlightedRegion);
				SelectedRtf = highlightedRegionOldRTF;
				highlightedRegion.length=0;
			}
#else
			if (IsHighlighted)
				unhighlightTimer.Stop();
			highlightedRegion.length=0;
#endif
		}
		public void HighlightRegion(Region r, Color c, bool autoClear) {
#if HIGHLIGHTBYCOLORING
			if (r.start!=highlightedRegion.start || r.length!=highlightedRegion.length)
				Unhighlight();
			highlightedRegion = r;
			highlightedRegionOldRTF = RegionRTF(r);
			SetRegionColor(r, c);
#else
			LockWindow();
			ScrollPoint s = ScrollPosition;
			highlightedRegion = r;
			SetRegion(r);
			if (autoClear)
				unhighlightTimer.Start();
			ScrollPosition = s;
			UnlockWindow();
#endif
		}

		// Highlight the expression before the selection.
		public void MaybeHighlightExpression() {
			if (!IsHighlighted) {
				if (userSelection.length==0 && userSelection.start>0 && userSelection.start<=Text.Length) {
					char charBefore = Text[userSelection.start-1];
					UnicodeCategory catBefore = char.GetUnicodeCategory(charBefore);
					if (catBefore==UnicodeCategory.ClosePunctuation || catBefore==UnicodeCategory.FinalQuotePunctuation
						&& charBefore != endOfCommentChar) {
						HighlightExpressionBefore(userSelection.start);
					}
				}
			}
		}

		// Highlight the expression starting at the selection
		public void MaybeHighlightAfter() {
			if (userSelection.length==0 && userSelection.start>0 && userSelection.start<=Text.Length-1) {
				char charBefore = Text[userSelection.start];
				UnicodeCategory catBefore = char.GetUnicodeCategory(charBefore);
				if (catBefore==UnicodeCategory.OpenPunctuation || catBefore==UnicodeCategory.InitialQuotePunctuation
					&& charBefore != startOfCommentChar) {
					HighlightExpressionAfter(userSelection.start);
				}
			}
		}

		public string RegionText(Region r) {
			if (r.length == 0) return ""; //null;
			else {
				//SetRegion(r);
				//return SelectedText;
				// This is *probably* faster and it doesn't require updating the selection,
				// which can cause unwanted blinking.
				
				string t=Text;
				if (r.start+r.length>t.Length)
					return t.Substring(r.start);
				else
					return t.Substring(r.start, r.length);
			}
		}
		public void SetRegionText(Region r, string newText) {
			Unhighlight();
			SetRegion(r);

			SelectedText = newText;
		}

		public string RegionRTF(Region r) {
			if (r.length == 0) return ""; //null;
			else {
				SetRegion(r);
				return SelectedRtf;
			}
		}
		public void SetRegionRTF(Region r, string newRTFText) {
			Unhighlight();
			SetRegion(r);

			SelectedRtf = newRTFText;
		}

		public Color RegionColor(Region r) {
			SetRegion(r);
			return SelectionColor;
		}
		public void SetRegionColor(Region r, Color c) {
			SetRegion(r);
			SelectionColor = c;
		}

		public Font RegionFont(Region r) {
			SetRegion(r);
			return SelectionFont;
		}
		public void SetRegionFont(Region r, Font f) {
			SetRegion(r);
			SelectionFont = f;
		}

		public Style RegionStyle(Region r) {
			return new Style(RegionFont(r), RegionColor(r));
		}

		public void SetRegionStyle(Region r, Style s) {
			SetRegionFont(r, s.font);
			SetRegionColor(r, s.color);
		}

		public int RegionCharOffset(Region r) {
			return SelectionCharOffset;
		}
		public void SetRegionCharOffset(Region r, int offset) {
			SetRegion(r);
			SelectionCharOffset = offset;
		}

		public int RegionIndent(Region r) {
			SetRegion(r);
			return SelectionIndent;
		}
		public void SetRegionIndent(Region r, int pixels) {
			SetRegion(r);
			if (SelectionIndent != pixels)
				SelectionIndent = pixels;
		}

		public bool RegionBullet(Region r) {
			SetRegion(r);
			return SelectionBullet;
		}

		public void SetRegionBullet(Region r, bool state) {
			SetRegion(r);
			SelectionBullet = state;
		}

		Region currentRegion;

		void SetRegion(Region r) {
			if (r.start != currentRegion.start || r.length != currentRegion.length) {
				SelectionLength=0;   // Keep it from accidentally running off the screen or buffer when changing start.
				SelectionStart = Math.Max(0, r.start);
				SelectionLength = Math.Max(0, r.length);
				currentRegion = r;
			}
		}

		public void ScrollToPosition(int position) {
			SetRegion(new Region(TextLength-1, 0));
			userSelection = new Region(position, 0);
			RestoreUserSelection();
		}

		//
		// Incrementally update whatever automatic formatting needs to be updated
		// after a key is typed.
		//
		protected void UpdateTextStyles(char keyChar) {
			switch (keyChar) {
				case ' ':
				case '\n':
				case '\r':
				case ']':
				case '[':
				case '(':
				case ')':
					if (keyChar=='(')
						keyChar = '[';
					CodeScanner sc = ScanCharsFrom(userSelection.start);
					// Find the start of the line, skipping any prompt
					sc.SkipBackwardToBOL();
					if (sc.CurrentChar==TopLevel.promptCarret) {
						sc.Forward();
						sc.SkipForwardWhiteSpace();
					}

					// Check if the line starts with "[def"
					if (!sc.LookingAt("[def") && !sc.LookingAt("(def"))
						// Not a definition
						break;
					// It's a definition; check to see if we're in title position.
					sc.Forward();									// Skip the bracket.
					sc.SkipForwardToken();					// Skip over def-whatever
					while (sc.charIndex<userSelection.start && (Char.IsWhiteSpace(sc.CurrentChar) || sc.CurrentChar=='[' || sc.CurrentChar =='('))
						sc.Forward();

					bool atTitle = userSelection.Start<=sc.charIndex;
					if (atTitle && keyChar=='[')
						nextStyle = TheStyle("Title");
					SetRegionStyle(userSelection, (keyChar!='[' && atTitle)?TheStyle("Title"):TheStyle("Normal"));
					// In theory, this should do it.

					//					SetRegionCharOffset(userSelection, 0);
					//					int endExp = userSelection.Start;
					//					CodeScanner sc = ScanCharsFrom(endExp);
					//					int startExp = sc.SkipBackwardExpression();
					//					if ((endExp-startExp) < 30) {
					//						if (RegionText(new Region(startExp, endExp-startExp)).StartsWith("define")) {
					//							if (keyChar=='[') {
					//								SetRegionStyle(userSelection, TheStyle("Normal"));
					//								nextStyle = TheStyle("Title");
					//							} else
					//								SetRegionStyle(userSelection, TheStyle("Title"));
					//						} else {
					//							endExp = startExp;
					//							startExp = sc.SkipBackwardExpression();
					//							if ((endExp-startExp) < 30 && RegionText(new Region(startExp, endExp-startExp)).StartsWith("define"))
					//								SetRegionStyle(userSelection, TheStyle("Normal"));
					//						}
					//						MarkUndo();
					//					}
					break;

				case '<':
					if (IsTypingAfter(startOfCommentString))
						SetRegionStyle(userSelection, TheStyle("Comment"));
					break;

				case ';':
					SetRegionStyle(userSelection, TheStyle("Comment"));
					break;

				case '>':
					if (IsTypingAfter(endOfCommentString))
						SetRegionStyle(new Region(userSelection.Start-1, userSelection.End-(userSelection.Start-1)), TheStyle("Normal"));
					break;

#if SYNTAXHIGHLIGHTING		
				case '<':
					if (EditorConfiguration.doComment & IsTypingAfter(startOfCommentString)) {
						int closeComment=ScanCharsFrom(userSelection.start).SkipForwardToChar(endOfCommentChar);
						if (closeComment==-1) {
							closeComment=TextLength;
						}	
						SetRegionColor(new Region(userSelection.start-1, closeComment-userSelection.start+2),
							EditorConfiguration.commentColor);
					}
					break;

				case '>':
					if (EditorConfiguration.doComment & IsTypingAfter(endOfCommentString)) {
						int nextComment=ScanCharsFrom(userSelection.start).SkipForwardToChar(startOfCommentChar);
						if (nextComment==-1) {
							nextComment=TextLength;
						}
						SetRegionColor(new Region(userSelection.start, nextComment-userSelection.start), this.ForeColor);
					}
					break;
#endif
			}
		}


		/// <summary>
		/// Paren-Matching Highlighting
		/// </summary>
		/// <param name="pos"></param>
		/// 

		void HighlightExpressionBefore(int pos) {
			HighlightRegion(ExpressionRegionBefore(pos), Color.LawnGreen, true);
		}

		void HighlightExpressionAfter(int pos) {
			HighlightRegion(ExpressionRegionAfter(pos), Color.LawnGreen, false);
		}

#if SYNTAXHIGHLIGHTING
		/// <summary>
		/// Syntax highlighting
		/// </summary>
		/// <param name="KeyWords"></param>
		/// <param name="key"></param>
		/// <returns></returns>
		//returns nomatch if the hashtable doesn't contain the keyword, 
		//returns the color associated with the keyword otherwise
		private string MatchKeyWord(Hashtable KeyWords, string key)
		{
			if (key != null && KeyWords.ContainsKey(key)) 
				return (string)KeyWords[key];
			else 
				return null;
		}

		//update all the highlighting
		//if keypress was a delim, check before and after it
		//if it wasn't a delim, check the current word

		private void HighlightUpdate(char keypress, int position) {
			if (!char.IsWhiteSpace(keypress) && !char.IsSeparator(keypress)) {
				SyntaxHighlightKeyword(CurrentWord(position));
			} else {
				SyntaxHighlightKeyword(WordToLeft(position));
				SyntaxHighlightKeyword(WordToRight(position));
			}
		}

		//syntaxhighlight the entire codebox
		public bool SyntaxHighlight() {
			NoteUserSelection();
			bool success = SyntaxHighlight(0, Lines);
			RestoreUserSelection();
			return success;
		}
				
		//syntax highlight a portion of the codebox
		//i have to admit, i have not tested this on any input but lines=Lines[]
		public bool SyntaxHighlight(int start, string[] lines) 
		{
			bool success=false;
			if (EditorConfiguration.canHighlight){ 
				int WordStartsAt = start, WordEndsBefore;
				CodeScanner cs = ScanCharsFrom(start);
				while(!cs.AtEndOfBuffer && 
					((WordEndsBefore = cs.SkipForwardToWordSeparator()) >= (int) Error.SUCCESSFUL)) {
					SyntaxHighlightKeyword(new Region(WordStartsAt, WordEndsBefore-WordStartsAt));
					cs.Forward();
					WordStartsAt = cs.charIndex;
				}
				success = true;
			}
			return success;
		}

		public bool SyntaxHighlight(int start, int end, string[] lines) {
			bool success=false;
			if (EditorConfiguration.canHighlight){ 
				int WordStartsAt = start, WordEndsBefore;
				CodeScanner cs = ScanCharsFrom(start);
				while(!cs.AtEndOfBuffer && (cs.charIndex < end) &&
					((WordEndsBefore = cs.SkipForwardToWordSeparator()) >= (int) Error.SUCCESSFUL)) {
					SyntaxHighlightKeyword(new Region(WordStartsAt, WordEndsBefore-WordStartsAt));
					cs.Forward();
					WordStartsAt = cs.charIndex;
				}
				success = true;
			}
			return success;
		}

		//syntax highlight a legitimate keyword
		int SyntaxHighlightKeyword(Region Word) {
			int status = -1;
			if (this.CommentInRegion(Word)|| this.InCommentedRegion(Word.start))
				return status;
			if (Word.length > 0) {
				string WordColor;
				if ((WordColor = MatchKeyWord(EditorConfiguration.keywords, RegionText(Word))) != null)  {
					//SetRegionColor(Word, Colors[WordColor]);
					SetRegionColor(Word, (Color)colorConverter.ConvertFromString(WordColor));
					status = 0;
				}
				else 
					SetRegionColor(Word, ForeColor);
			}
			return status;
		}

		public void SyntaxUnhighlight() {
			SetRegionColor(new Region(0, TextLength), ForeColor);
		}

		//Comment Highlight Everything
		int HighlightComments() {
			//HighlightComments();
			return 0;
		}

		//Comment Highlight a Phrase
		int HighlightComments(int start, int end) {
			return 0;
		}

		//rachel, jeff, mazen added comment stuff
		
		public void CommentRegion(Region r)
		{
			CodeScanner cs;
			int endcomment=r.start;
			int end=r.start+r.length;
			while (endcomment<end)
			{
				cs=ScanCharsFrom(endcomment);
				int startcomment=cs.SkipForwardToChar(startOfCommentChar);
				if (startcomment==-1)//if we cant find a comment set the region black and quit
				{
					SetRegionColor(new Region(endcomment, r.length), this.ForeColor);
					SyntaxHighlight(endcomment, end, Lines);
					endcomment=end;
					break;
				}
				//gotta change the whole thing to forecolor first then syntax highlight, those are just the rules
				SetRegionColor(new Region(endcomment, startcomment-endcomment),
					this.ForeColor);
				SyntaxHighlight(endcomment, startcomment+1, Lines);
				

				int closingbracket=cs.SkipForwardToChar(endOfCommentChar);
				Region c;
				c.start=startcomment;
				if (closingbracket!=-1)
					endcomment=closingbracket;
				else
					endcomment=end;
				c.length=endcomment-startcomment+1; 
				SetRegionColor(c, EditorConfiguration.commentColor);//this color is now an option
				endcomment++;
			}
		}

		public void ReComment()
		{
			CodeScanner cs=ScanCharsFrom(userSelection.start);
			int prevComment=cs.SkipBackwardToToken(startOfCommentChar); //find last start of comment
			if (prevComment==-1)
				prevComment=0;		//if we can't find that, start from the beginning
			cs=ScanCharsFrom(userSelection.start);
			int nextComment=cs.SkipForwardToChar(startOfCommentChar); //find next comment and call comment region
			if (nextComment==-1)
				nextComment=TextLength;
			Meta.Editor.Region commentMe;
			commentMe.start=prevComment;
			commentMe.length=nextComment-prevComment+1;
			CommentRegion(commentMe);
			reComment=false;
		}

		public bool InCommentedRegion(int start)
		{
			CodeScanner cs=ScanCharsFrom(start);
			int openbrak=cs.SkipBackwardToToken(startOfCommentChar);
			cs=ScanCharsFrom(start);
			int closebrak=cs.SkipBackwardToToken(endOfCommentChar);
			return openbrak > closebrak; //if open brak is closer then you're in a comment
		}

		public bool CommentInRegion(Region Word)
		{
			int start = Word.start;
			int length = Word.length;
			string mystr = RegionText(new Region(start, length+1));
			foreach (char c in mystr.ToCharArray(0, mystr.Length))
			{
				if (c.Equals(endOfCommentChar) || c.Equals(startOfCommentChar))
					return true;
			}
			return false;
		}

		public void ReCommentAll(){
			NoteUserSelection();
			CommentRegion(new Region(0, this.TextLength));
			RestoreUserSelection();
		}
#endif
	
		/// <summary>
		/// Crappy undo support
		/// This is *so* *so* ugly.
		/// 
		public void MarkUndo() {
			Unhighlight();
			if (ModifiedSinceUndoMark && undoEnabled) {
				if (undoStack.Count>0) {
					UndoRecord top = (UndoRecord)undoStack.Peek();
					top.scrollPosition = ScrollPosition;
					top.selection = userSelection;
				}
				string kluge = Rtf;

				if (kluge.Length>50000)
					undoEnabled = false;
				else
					undoStack.Push(new UndoRecord(userSelection, ScrollPosition, Rtf));  // save the whole *ing buffer as an RTF string...
			}
			ModifiedSinceUndoMark = false;
		}

		public void MyUndo() {
			if (undoStack.Count>0) {
				UndoRecord lastMod = (UndoRecord)undoStack.Peek();
				if (undoStack.Count>1)
					undoStack.Pop();

				userSelection = lastMod.selection;
				LockWindow();
				Rtf = lastMod.bufferRTF;     // restore the whole *ing buffer.
				RestoreUserSelection();
				ScrollPosition = lastMod.scrollPosition;
				UnlockWindow();
			}
			ModifiedSinceUndoMark = false;
		}

		public class UndoRecord {
			public Region selection;
			public string bufferRTF;  // kluge!
			public ScrollPoint scrollPosition;
			public UndoRecord(Region selection, ScrollPoint scrollPosition, string bufferRTF) {
				this.selection = selection;
				this.scrollPosition = scrollPosition;
				this.bufferRTF = bufferRTF;
			}
		}

		//const int SB_VERT = 1; 
		const int EM_GETSCROLLPOS = 0x0400 + 221; 
		const int EM_SETSCROLLPOS = 0x0400 + 222; 
  
		//[DllImport("user32", CharSet=CharSet.Auto)] 
		//public static extern bool GetScrollRange(IntPtr hWnd, int nBar, out int lpMinPos, out int lpMaxPos); 
 
		[DllImport("user32", CharSet=CharSet.Auto)] 
		public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, ScrollPoint lParam); 
 
		[StructLayout(LayoutKind.Sequential)] 
			public class ScrollPoint { 
			public int x; 
			public int y; 
 
			public ScrollPoint() { 
			} 
 
			public ScrollPoint(int x, int y) { 
				this.x = x; 
				this.y = y; 
			} 
		} 

		public ScrollPoint ScrollPosition {
			set {
				SendMessage(Handle, EM_SETSCROLLPOS, 0, value);
			}
			get {
				ScrollPoint p = new ScrollPoint();
				SendMessage(Handle, EM_GETSCROLLPOS, 0, p);
				return p;
			}
		}

		public int TopOfScreenScrollTarget = 80;
		public void ScrollToTop() {
			ScrollToTop(userSelection.start);
		}
		public void ScrollToTop(int index) {
			ScrollPoint s = ScrollPosition;
			Point currentPosition = GetPositionFromCharIndex(index);
			s.y -= TopOfScreenScrollTarget-currentPosition.Y;
			if (s.y>=0)
				ScrollPosition = s;
		}

		public void ScrollToCenter() {
			ScrollToCenter(userSelection.start);
		}
		public void ScrollToCenter(int index) {
			ScrollPoint s = ScrollPosition;
			Point currentPosition = GetPositionFromCharIndex(index);
			s.y -= ((Height-60)/2)-currentPosition.Y;
			if (s.y>=0)
				ScrollPosition = s;
		}

		#region Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent() {
			components = new System.ComponentModel.Container();
		}
		#endregion
	}
}